/**
 * 
 */
package jvs.vfs.opt.contact;

import java.util.List;
import java.util.Set;

import jvs.vfs.FileSystem;
import jvs.vfs.opt.contact.eclair.ContactsContract;
import android.content.Context;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Build;
import android.provider.Contacts;
import android.provider.Contacts.PeopleColumns;

/**
 * 
 * @author qiangli
 * 
 */
public abstract class ContactAdapter {
	private static final int __VERSION_CODES_ECLAIR = 5;

	private static ContactAdapter adapter;

	protected static boolean DEBUG = FileSystem.DEBUG;

	public static boolean newContactAPI = (Integer.parseInt(Build.VERSION.SDK) >= __VERSION_CODES_ECLAIR);

	static final String TAG = "ContactAdapter";
	
	public static final String SORT_BY_DISPLAY_NAME = sortby(0);
	public static final String SORT_BY_LAST_TIME_CONTACTED = sortby(1);
	public static final String SORT_BY_TIMES_CONTACTED = sortby(2);

	private static String sortby(int i) {
		// 0 name, 1 recent, 2 most
		final String[] bys = new String[] {
				PeopleColumns.DISPLAY_NAME + " ASC, " + Contacts.Phones.TYPE
						+ " ASC ",
				PeopleColumns.LAST_TIME_CONTACTED + " DESC, "
						+ PeopleColumns.DISPLAY_NAME + " ASC, "
						+ Contacts.Phones.TYPE + " ASC ",
				PeopleColumns.TIMES_CONTACTED + " DESC, "
						+ PeopleColumns.DISPLAY_NAME + " ASC, "
						+ Contacts.Phones.TYPE + " ASC " };
		//
		final String[] bys2 = new String[] {
				ContactsContract.Contacts.DISPLAY_NAME + " ASC, "
						+ ContactsContract.CommonDataKinds.Phone.TYPE + " ASC ",
				ContactsContract.Contacts.LAST_TIME_CONTACTED + " DESC, "
						+ ContactsContract.Contacts.DISPLAY_NAME + " ASC, "
						+ ContactsContract.CommonDataKinds.Phone.TYPE + " ASC ",
				ContactsContract.Contacts.TIMES_CONTACTED + " DESC, "
						+ ContactsContract.Contacts.DISPLAY_NAME + " ASC, "
						+ ContactsContract.CommonDataKinds.Phone.TYPE + " ASC " };
		
		return (newContactAPI ? bys2[i] : bys[i]);
	}
	
	public static Uri buildContactUri(long contactId) {
		Uri uri = (newContactAPI ? ContactsContract.RawContacts.CONTENT_URI
				: Contacts.People.CONTENT_URI);
		return uri.buildUpon().appendPath(contactId + "").build();
	}

	public static ContactAdapter getAdapter() {
		if (adapter == null) {
			try {
				adapter = (newContactAPI ? new ContactAdapter2()
						: new ContactAdapter1());
			} catch (Exception e) {
				throw new IllegalStateException(e);
			}
		}
		return adapter;
	}

	protected abstract long getContactId(Context ctx, String name);

	/**
	 * Get email from contact first, then from group if name is a group name and
	 * it is not found in contact
	 * 
	 * @param context
	 * @param name
	 * @return
	 */
	public String[] getEmails(Context ctx, String name) {
		if (name == null) {
			return null;
		}
		String[] emails = getEmailsInContact(ctx, name);
		if (emails == null && name.startsWith("@")) {
			return GroupContacts.getEmails(ctx, name);
		}
		return emails;
	}

	protected abstract String[] getEmailsInContact(Context ctx, String name);

	/**
	 * Get group or contact id
	 * 
	 * @param context
	 * @param name
	 * @return
	 */
	public long getId(Context ctx, String name) {
		if (name == null) {
			return -1;
		}
		long id = getContactId(ctx, name);
		if (id == -1 && name.startsWith("@")) {
			return GroupContacts.getIdByName(ctx, name);
		}
		return id;
	}

	/**
	 * Check email in contact and group
	 * 
	 * @param context
	 * @param email
	 * @return
	 */
	public boolean hasEmail(Context ctx, String email) {
		// check contact first as this results in better performance
		// 1. if we have one, most likely it is found in contacts
		// 2. group stores emails as one string, it is a sequential lookup
		if (hasEmailInContact(ctx, email)) {
			return true;
		}
		return GroupContacts.hasEmailInGroup(ctx, email);
	}

	protected abstract boolean hasEmailInContact(Context ctx, String email);

	protected abstract void loadContactsInContact(Context ctx,
			Set<ContactInfo> contacts);

	/**
	 * Load contact and group, shadow/ignore entries in group with identical
	 * names
	 * 
	 * @param context
	 * @param contacts
	 */
	public void loadEmailContacts(Context ctx, Set<ContactInfo> contacts) {
		// load from contact first and shadow entries in group with same name
		// (if any)
		loadContactsInContact(ctx, contacts);
		// group contact
		GroupContacts.loadGroups(ctx, contacts);
	}

	public void loadEmailFavorite(Context ctx, Set<ContactInfo> contacts) {
		loadFavoriteInContact(ctx, contacts);
	}

	protected abstract void loadFavoriteInContact(Context ctx, Set<ContactInfo> contacts);

	protected abstract void loadGroupNamesInConact(Context ctx,
			Set<String> names);

	/**
	 * Load aggregated lists from both contact and group
	 * 
	 * @param context
	 * @param lists
	 */
	public void loadListNames(Context ctx, Set<String> names) {
		// TODO get rid of lists without email addresses?
		// load from both contact and group
		loadGroupNamesInConact(ctx, names);
		GroupContacts.loadGroupNames(ctx, names);
	}

	public abstract void loadPhoneContacts(Context ctx,
			List<ContactInfo> contacts, int idx, String by);

	public abstract void loadPhoneFavorite(Context ctx,
			List<ContactInfo> contacts, int idx, String by);

	public Bitmap loadPhoto(Context ctx, long id) {
		return null;
	}
}
